import {
	RequestOptions,
	RequestResultType,
	RequestState,
	ResultFunction,
	RequestStatus,
} from "@/types";

const DEFAULT_OPTIONS: RequestOptions<unknown> = {
	retryTimes: 3,
	retryDelay: 1000,
	timeout: 30000,
	autoRetry: false,
};

/**
 * 自定义钩子，用于管理异步请求及其状态
 *
 * 该钩子封装了一个请求函数，提供了一系列配置选项和方法来管理请求的生命周期、错误处理、重试机制等
 *
 * @param request 一个接受参数对象并返回Promise的请求函数
 * @param defaultParams 默认的请求参数对象，所有请求都会合并这些参数
 * @param options 配置选项，包括超时、自动重试等设置，默认为DEFAULT_OPTIONS
 * @returns 返回一个对象，包含请求状态、提交请求函数、异步提交请求函数和取消请求函数
 */
export function useRequest<TData, TParams>(
	request: (params?: TParams) => Promise<RequestResultType<TData>>,
	defaultParams?: TParams,
	options?: Partial<RequestOptions<TParams>>
) {
	const mergedOptions = { ...DEFAULT_OPTIONS, ...options } as RequestOptions<TParams>;

	// 标记组件是否已卸载，用于在请求期间检查组件状态
	let isUnmounted = false;
	// 用于取消请求的控制器
	const abortController = new AbortController();

	// 请求状态对象，用于存储请求的当前状态
	const requestState = reactive<RequestState>({
		isLoading: false,
		isError: false,
		errorMessage: undefined,
		retryCount: 0,
	});

	/**
	 * 格式化请求参数
	 *
	 * 该函数合并默认参数和传入参数，并格式化特定参数值
	 * 主要用于在请求前准备参数，以适应请求函数的需求
	 *
	 * @param params 可选的请求参数对象，将与默认参数合并
	 * @returns 返回格式化后的参数对象
	 */
	const formatParams = (params?: TParams) => {
		const p = Object.assign({}, defaultParams, params) as Record<string, any>;

		if (!p) return p;

		return Object.keys(p).reduce((acc, key) => {
			const value = p[key];
			if (value === null || value === undefined) return acc;

			acc[key] = String(value).replace("%date%", "20241101");
			return acc;
		}, {} as Record<string, any>) as TParams;
	};

	/**
	 * 处理响应结果
	 *
	 * 该函数根据响应结果更新请求状态，并调用相应的回调函数
	 * 主要负责处理请求成功、认证失败、请求错误等情况
	 *
	 * @param result 请求结果对象，包含响应数据和状态码
	 * @param cb 请求结果处理回调函数，包含成功、错误和完成的处理函数
	 */
	const handleResponse = <T>(result: RequestResultType<T>, cb: ResultFunction<T>): void => {
		if (isUnmounted) return;

		if (typeof result.code === "undefined") {
			console.error("Invalid response format:", result);
			requestState.isError = true;
			requestState.errorMessage = "Invalid response format";
			return;
		}

		if (result.code === RequestStatus.SUCCESS) {
			requestState.isError = false;
			requestState.errorMessage = undefined;
			cb.onSuccess?.(result.data, result);
		} else if (result.code === RequestStatus.NO_AUTH) {
			// 处理认证失败
			window.location.href = "/login";
		} else {
			requestState.isError = true;
			requestState.errorMessage = result.message;
			cb.onError?.(result);
		}

		cb.onComplete?.(result);
	};

	/**
	 * 处理请求错误
	 *
	 * 该函数根据错误类型更新请求状态，并调用错误回调函数
	 * 主要负责处理请求错误和中止请求的情况
	 *
	 * @param error 错误对象，包含错误信息
	 * @param cb 请求结果处理回调函数，包含成功、错误和完成的处理函数
	 */
	const handleError = (error: Error, cb: ResultFunction<TData>): void => {
		if (isUnmounted) return;

		requestState.isError = true;
		requestState.errorMessage = error.message;

		if (error.name === "AbortError") {
			requestState.errorMessage = "请求已取消";
		} else if (error.message === "Failed to fetch") {
			requestState.errorMessage = "网络连接失败";
		}

		cb.onError?.(error);
		cb.onComplete?.(error);
	};

	/**
	 * 执行请求函数
	 *
	 * 该函数负责发起请求，并处理请求的响应、错误以及超时逻辑
	 * 它还实现了自动重试机制，并确保在组件卸载时不会继续执行请求
	 *
	 * @param cb 请求结果处理回调函数
	 * @param params 可选的请求参数对象
	 * @param signal 可选的AbortSignal，用于中止请求
	 */
	const executeRequest = async (
		cb: ResultFunction<TData>,
		params?: TParams,
		signal?: AbortSignal
	): Promise<void> => {
		try {
			// 格式化请求参数，以适应请求函数的需求
			const formattedParams = formatParams(params);
			// 使用Promise.race发起请求，并设置超时
			const result = (await Promise.race([
				request(formattedParams),
				new Promise<never>((_, reject) =>
					setTimeout(() => reject(new Error("请求超时")), mergedOptions.timeout)
				),
			])) as RequestResultType<TData>;

			// 处理响应结果
			handleResponse(result, cb);
		} catch (error) {
			// 自动重试机制
			if (
				mergedOptions.autoRetry &&
				requestState.retryCount < (mergedOptions.retryTimes || 0) &&
				!(error instanceof Error && error.name === "AbortError")
			) {
				requestState.retryCount++;
				await new Promise(resolve => setTimeout(resolve, mergedOptions.retryDelay));
				return executeRequest(cb, params, signal);
			}

			// 处理请求错误
			handleError(error as Error, cb);
		} finally {
			// 确保在组件卸载时不会继续执行请求
			if (!isUnmounted) {
				requestState.isLoading = false;
			}
		}
	};

	/**
	 * 提交请求时的处理函数
	 * 该函数主要用于初始化请求状态，并执行实际的请求
	 *
	 * @param cb 请求完成后的回调函数，用于处理请求结果
	 * @param params 可选的请求参数，用于定制请求
	 */
	const onSubmitRequest = (cb: ResultFunction<TData>, params?: TParams): void => {
		// 检查组件是否已卸载，若卸载则不执行后续操作
		if (isUnmounted) return;

		// 设置请求状态为加载中
		requestState.isLoading = true;
		// 重置错误状态
		requestState.isError = false;
		// 清空错误信息
		requestState.errorMessage = undefined;
		// 重置重试次数
		requestState.retryCount = 0;

		// 执行请求，并传入回调函数、请求参数和中止信号
		executeRequest(cb, params, abortController.signal);
	};

	/**
	 * 异步提交请求函数
	 *
	 * 该函数负责发起一个异步请求，并在请求期间管理请求状态
	 * 它接受一个回调函数和可选的参数对象作为输入
	 *
	 * @param cb - ResultFunction类型的回调函数，用于处理请求结果
	 * @param params - 可选的参数对象，包含请求所需的参数
	 * @returns 无返回值
	 */
	const onAsyncSubmitRequest = async (
		cb: ResultFunction<TData>,
		params?: TParams
	): Promise<void> => {
		// 检查组件是否已卸载，如果已卸载，则不执行任何操作
		if (isUnmounted) return;

		// 请求开始时，设置请求状态
		requestState.isLoading = true;
		requestState.isError = false;
		requestState.errorMessage = undefined;
		requestState.retryCount = 0;

		// 执行请求，并传入回调函数、参数对象以及abort信号
		await executeRequest(cb, params, abortController.signal);
	};

	/**
	 * 中止请求函数
	 * 该函数用于中止当前正在进行的请求，并重置请求状态
	 * 主要通过调用AbortController的abort方法来中止请求，同时更新请求状态isLoading为false
	 */
	const cancelRequest = () => {
		// 中止当前请求
		abortController.abort();
		// 更新请求状态为非加载中
		requestState.isLoading = false;
	};

	// 在组件卸载时调用该函数，以确保所有请求都被取消
	onUnmounted(() => {
		isUnmounted = true;
		cancelRequest();
	});

	// 返回请求状态和请求管理函数
	return {
		requestState,
		onSubmitRequest,
		onAsyncSubmitRequest,
		cancelRequest,
	};
}
